/**
 * 无限层级联动
 *
 * options 参数对象：
 *
 *     selects 各层级对应select控件的查询名 #id 或 .class
 *     fnData 完整数据或可返回各层数据的函数
 *     callback 选择变动后的回调函数，回调参数为以选择的值和文本组成的数组
 *
 * fnData为对象时的数据结构
 *
 * {
 *     'key1': {
 *         text: '键1',
 *         value: 'key1',
 *         children: {
 *             'key3': {
 *                 text: '键3',
 *                 value: 'key3',
 *             },
 *             ...
 *         }
 *     },
 *     ...
 * }
 *
 *
 * @returns {Selects}
 */

var Selects = function (options) {
    
    this.level = options.selects.length;
    
    this.callback = options.callback;
    
    this.Events = new Array(options.selects.length - 1);
    
    this.fnData = options.fnData;
    
    this.placeholders = options.placeholders;
    
    this.defaults = options.defaults;
    
    this.selects = [];
    for (var i = 0; i < options.selects.length; i++) {
        this.selects.push(document.querySelector(options.selects[i]));
    }
    
    function addListener(element, e, fn) {
        element.addEventListener ? element.addEventListener(e, fn, false) : element.attachEvent("on" + e, fn);
    }
    
    function removeListener(element, e, fn) {
        element.removeEventListener ? element.removeEventListener(e, fn, false) : element.detachEvent("on" + e, fn);
    }
    
    this.getChildren = (fnData, level, callback) => {
        
        var keys = [];
        
        for (var i = 0; i < level; i++) {
            keys.push(this.selects[i].value);
        }
        
        if (typeof (fnData) == 'function') {
            fnData(keys, (data) => {
                callback(data);
            });
        } else {
            
            function recursive(children, i) {
                
                if (i < level) {
                    
                    var key = keys[i];
                    
                    if (typeof (children[key]) == 'object' &&
                        children[key].hasOwnProperty('children') &&
                        typeof children[key].children == 'object' &&
                        Object.values(children[key].children).length > 0) {
                        
                        i++;
                        
                        return recursive(children[key].children, i);
                        
                    } else {
                        
                        return {};
                        
                    }
                    
                } else {
                    return children;
                }
                
            }
            
            callback(recursive(fnData, 0));
            
        }
    };
    
    this.addOptions = (select, children, level) => {
        for (var key in children) {
            if (typeof (children[key]) == 'object') {
                var option = new Option(children[key].text, key);
                if (this.defaults && this.defaults.length > 0 && this.defaults[level] && this.defaults[level] == key) {
                    option.selected = true;
                }
                select.options.add(option);
            }
        }
    };
    
    this.sendCallback = () => {
        
        if (this.callback) {
            
            var cbData = [];
            for (var i = 0; i < this.selects.length; i++) {
                if (this.selects[i].options.length > 0) {
                    cbData.push({'value': this.selects[i].value, 'text': this.selects[i].options[this.selects[i].options.selectedIndex].text});
                } else {
                    cbData.push({'value': '', 'text': ''});
                }
            }
            
            this.callback(cbData);
            
        }
    };
    
    this.Change = (obj, fnData, level) => {
        
        for (var i = level + 1; i < this.selects.length; i++) {
            
            this.selects[i].options.length = 0;
            
            if (this.placeholders && this.placeholders.length > 0 && this.placeholders[level + 1]) {
                this.selects[i].options.add(new Option(this.placeholders[i], ''));
            }
        }
        
        if (level == this.level - 1 || obj.value == '') {
            
            this.sendCallback();
            
            return;
            
        }
        
        this.getChildren(fnData, level + 1, (children) => {
            
            var select = this.selects[level + 1];
            
            if (!children) {
                return;
            }
            
            if (this.Events[level] != undefined) {
                removeListener(select, 'change', this.Events[level]);
            }
            
            this.Events[level] = () => {
                this.Change(select, fnData, level + 1);
            };
            
            addListener(select, 'change', this.Events[level]);
            
            this.addOptions(select, children, level + 1);
            
            this.Change(select, fnData, level + 1);
            
        });
        
    };
    
    var select = this.selects[0];
    
    this.Events[0] = () => {
        this.Change(select, this.fnData, 0);
    };
    
    removeListener(select, 'change', this.Events[0]);
    
    addListener(select, 'change', this.Events[0]);
    
    this.getChildren(this.fnData, 0, (children) => {
        
        if (this.placeholders && this.placeholders.length > 0 && this.placeholders[0]) {
            this.selects[0].options.add(new Option(this.placeholders[0], ''));
        }
        
        this.addOptions(select, children, 0);
        
        this.Change(select, this.fnData, 0);
        
    });
    
};
